home *** CD-ROM | disk | FTP | other *** search
- interrupts segment at 0h
- org 1ch*4 ; This is to use INT 1Ch
- timer_int label dword ; which is the timer interupt
- interrupts ends
-
- screen segment at 0b000h ; A dummy segment to use
- screen ends ; as the Extra Segment
-
- code_seg segment
- assume cs:code_seg
- org 100h ; org = 100 to make this into
- ; a .COM file
- first: jmp load_clock
-
- old_time_int dd ? ; The address INT 1Ch normally uses
- count500 dw 0 ; Used to update clock every 500 counts
- cursor dw 0 ; Location of the cursor on the screen
- video_port dw ? ; Video status port - ckeck for scanning
- display dw 5 dup(133ah) ; Initial value for the clock
- dw 3 dup(1320h) ; Add 3 ' 's for AM/PM.
-
- clock proc near ; The timer INT will now come here
-
- push ax ; Save the registers - good form
- push cx
- push di
- push si
- push es
-
- pushf ; First call old time interrupt
- call old_time_int
-
- mov cx,count500 ; Prepare to test if we
- inc cx ; should recalculate the time - done
- cmp cx,500 ; every 500 timer counts
- jb dont_recalculate
- call calc ; Recalculate the time
- mov cx,0 ; Reset Count500
- dont_recalculate:
- mov count500,cx ; Store incremented or cleared value
-
- assume es:screen ; Set up screen as the Extra Segment
- mov cx,screen
- mov es,cx
- mov dx,video_port ; This is the screen status port
- mov di,cursor ; Set up cursor on screen as destination
- lea si,display ; Set up the display in memory as source
- mov cx,8 ; To move char only
-
- cli
-
- scan_low: ; Start waiting for a new horizontal scan
- in al,dx ; i.e. the the vidio controller scan
- test al,1 ; status is low.
- jnz scan_low
-
- mov ah,cs:[si] ; Move byte to be written into AH
-
- scan_high: ; After port has gone low, it must go high
- in al,dx ; before it is safe to write directly
- test al,1 ; to the screen buffer in memory
- jz scan_high
-
- mov es:[di],ah ; Move to screen one byte at a time.
- inc di ; Position to attribute byte
- inc si ; on screen.
- inc di ; Increment again to position
- inc si ; past the attribute byte
-
- loop scan_low ; Go back foe next byte
- sti
-
- pop es ; Here are required pops to exit
- pop si
- pop di
- pop cx
- pop ax
-
- iret ; Aninterrupt needs an IRET
-
- clock endp
-
- calc proc near ; Here we recalculate the time and store it
- push ax ; Puushes to save everytheing that
- push bx ; gets destroyed
- push cx
- push dx
-
- xor ax,ax ; Set up for clock read.
- int 1ah ; Read the clock.
- push dx ; Save low(minutes) portion.
- mov ax,cx ; Move high(hours) portion to AX.
-
-
-
- check: cmp ax,24 ; Make sure it's less than 24
- jle valid_time
- sub ax,24 ; If not, keep subtracting until it is.
- jmp check
- valid_time:
- lea bx,display ; Set up BX as pointer to display in memory
- mov byte ptr cs:[bx+14],'M' ; Move the 'M' into the display.
-
- cmp ax,0 ; Is it between 12 & 1 AM?
- jne after_1am ; No, check other times.
- mov ax,012 ; Yes, make it '12',
- jmp am ; and set to display 'AM'
- after_1am:
- cmp ax,12 ; Is it between 1 & 12 AM?
- jl am ; Yes, set to display 'AM'
- cmp ax,12 ; Is it between 12 & 1 PM?
- je pm ; Yes, set to display 'PM'
- sub ax,12 ; If not, take off another 12.
- cmp ax,12 ; Is it after midnight?
- je am ; Yes, set to display 'AM'
- pm: mov byte ptr cs:[bx+12],'P' ; Otherwise move 'P' into the display.
- jmp am_pm_done ; Continue.
- am: mov byte ptr cs:[bx+12],'A' ; Move an 'A' into the display.
- am_pm_done:
- aam ; Convert AX to BCD - a nice command
- add ax,3030h ; Add '0' to both bytes in AX to make ASCII
- cmp ah,'0' ; Is the 1st diget '0'?
- jne dont_edit ; Then don't blank the character.
- mov ah,' ' ; Otherwise, put a space in AH.
- dont_edit:
- mov cs:[bx],ah ; Move first hours digit into display
- mov cs:[bx+2],al ; Then the second digit
-
- pop ax ; Get low portion of clock from stack.
- mov cx,8 ; We want to multiply minutes by 60 and
- shr ax,cl ; divide by 65536, so multiply by 60 and shift
- mov dx,60 ; right 16 bits. 60 secs = 65536 ticks.
- mul dl
- shr ax,cl
- aam ; Again convert AX to Binary Coded Decimal
- add ax,3030h ; Add to make two ASCII characters
- mov cs:[bx+6],ah ; and move them into the display in memory
- mov cs:[bx+8],al
-
- ; Restore registers
- pop dx
- pop cx
- pop bx
- pop ax
-
- ret
- calc endp
-
- load_clock proc near ; This procedure initializes everything
- assume ds:interrupts ; The Data Segment will be the interrupt area
- mov ax,interrupts
- mov ds,ax
- cli
-
- mov ax,timer_int ; Get the old interrupt service routine
- mov old_time_int,ax ; address and put it into our location
- mov ax,timer_int[2] ; OLD_TIME_INT so we can still call it
- mov old_time_int[2],ax
-
- mov timer_int,offset clock ; Now load the address of our clock
- mov timer_int[2],cs ; routine into TIMER_INT so the timer
- ; interrupt will call CLOCK
- sti
-
- mov ah,15 ; Ask for service 15 of INT 10H
- int 10h ; This tells us how the display is set up
- sub ah,20 ; Move to twenty places before edge
- shl ah,1 ; Mult by two (char and attribute bytes)
- mov byte ptr cursor,ah ; Move cursor to it's memory location
- mov video_port,03bah ; Assume this is a monochrome display
- test al,4 ; Is it?
- jnz get_time ; Yes - jump out
- add cursor,8000h ; No - set up for graphics display
- mov video_port,03dah
-
- get_time:
- call calc ; This is to avoid showing 00:00 for first 500 counts
- mov dx,offset load_clock ; Set up for everything but LOAD_CLOCK
- int 27h ; to stay attached to DOS
-
- load_clock endp
-
- code_seg ends
-
- end first ; END FIRST so 8088 will go to FIRST first
-